package core

import (
	"fmt"
	"gin-admin/common/dto/response"
	"gin-admin/global"
	"gin-admin/utils"
	"strings"

	"gorm.io/gorm"
)

func GenericCreateWithModel(db *gorm.DB, data interface{}) *response.ServiceError {
	result := db.Create(data)
	if err := HandelError(result); err != nil {
		return err
	}
	return nil
}

func GenericLogicalDelete(db *gorm.DB, model interface{}) *response.ServiceError {
	result := db.Model(model).Update("is_delete", true)
	if err := HandelError(result); err != nil {
		return err
	}
	return nil
}

func GenericUpdateWithModel(db *gorm.DB, model interface{}, columns interface{}) *response.ServiceError {
	result := db.Model(model).Updates(columns)
	if err := HandelError(result); err != nil {
		return err
	}
	return nil
}

func GenericQueryByField(db *gorm.DB, filed, value string, model interface{}, notFoundHandler func() *response.ServiceError) (*gorm.DB, *response.ServiceError) {
	condition := map[string]interface{}{
		filed:       value,
		"is_delete": false,
	}
	errMessage := response.GetStatusCode(response.FailedCode).Message
	result := db.Where(condition).First(&model)
	if result.RowsAffected <= 0 {
		if notFoundHandler != nil {
			err := notFoundHandler()
			return result, err
		}

		return result, response.WrapError(response.FailedCode, errMessage)
	}
	if err := HandelError(result); err != nil {
		return result, err
	}
	return result, nil
}

func GenericQueryById(db *gorm.DB, id string, model interface{}, notFoundHandler func() *response.ServiceError) (*gorm.DB, *response.ServiceError) {
	return GenericQueryByField(db, "id", id, model, notFoundHandler)
}

func GenericQueryByUid(db *gorm.DB, uid string, model interface{}, notFoundHandler func() *response.ServiceError) (*gorm.DB, *response.ServiceError) {
	return GenericQueryByField(db, "uid", uid, model, notFoundHandler)
}

func GenericQueryByCondition(db *gorm.DB, condition map[string]interface{}, model interface{}, notFoundHandler func() *response.ServiceError) *response.ServiceError {
	condition["is_delete"] = false
	result := db.Where(condition).Find(model)
	if err := HandelError(result); err != nil {
		return err
	}
	if result.RowsAffected <= 0 && notFoundHandler != nil {
		err := notFoundHandler()
		return err
	}
	return nil
}

func GenericQueryTakeByCondition(db *gorm.DB, condition map[string]interface{}, method interface{}, model interface{}, notFoundHandler func() *response.ServiceError) *response.ServiceError {
	var query []string
	var queryString string
	for key, value := range condition {
		query = append(query, fmt.Sprintf("\"%v\" = '%v'", key, value))
	}
	if method == nil {
		method = "AND"
	}
	switch method {
	case "AND":
		queryString = fmt.Sprintf("(%v) AND is_delete = %v", strings.Join(query, " AND "), false)
	default:
		queryString = fmt.Sprintf("(%v) AND is_delete = %v", strings.Join(query, " OR "), false)
	}
	result := db.Where(queryString).Take(model)
	if err := HandelError(result); err != nil {
		return err
	}
	if result.RowsAffected <= 0 && notFoundHandler != nil {
		err := notFoundHandler()
		return err
	}
	return nil
}

func GenericQueryByPagination(db *gorm.DB, pagination interface{}, model interface{}) (*response.PaginationResponse, *response.ServiceError) {
	paginationMap, err := utils.StructToMapWithoutZero(pagination)
	paginationMap["is_delete"] = false
	if err != nil {
		return nil, err
	}
	page, size := 1, 10
	if p, ok := paginationMap["page"].(int); ok {
		page = p
	}
	if s, ok := paginationMap["size"].(int); ok {
		size = s
	}
	offset := (page - 1) * size
	delete(paginationMap, "page")
	delete(paginationMap, "size")
	query := db.Where(paginationMap)
	result := query.Model(model).Limit(size).Offset(offset).Find(model)
	if err := HandelError(result); err != nil {
		return nil, err
	}

	var total int64
	result = query.Model(model).Count(&total)
	if err := HandelError(result); err != nil {
		return nil, err
	}

	var responseData = response.PaginationResponse{
		Page:  page,
		Size:  size,
		Total: total,
	}
	return &responseData, nil
}

func CheckDataExistsByCondition(db *gorm.DB, condition interface{}, model interface{}) (bool, *response.ServiceError) {
	var query *gorm.DB
	switch cond := condition.(type) {
	case map[string]interface{}:
		if cond == nil {
			cond = make(map[string]interface{})
		}
		cond["is_delete"] = false
		query = db.Where(cond)
	case interface{}:
		query = db.Where(cond).Where("is_delete = ?", false)
	default:
		return false, response.WrapError(response.FailedCode, "不支持的数据类型")
	}

	result := query.First(model)
	if err := HandelError(result); err != nil {
		return false, err
	}

	return result.RowsAffected > 0, nil
}

func BuildQueryCondition(args ...interface{}) map[string]interface{} {
	result := make(map[string]interface{})
	switch len(args) {
	case 1:
		if m, ok := args[0].(map[string]interface{}); ok {
			return m
		}
	case 2:
		if key, ok := args[0].(string); ok {
			result[key] = args[1]
			return result
		}
	default:
		return nil
	}
	global.GA_LOGGER.Sugar().Panicf("构造请求参数发生错误")
	return nil
}

func UseTransactionWith(db *gorm.DB, fn func(tx *gorm.DB) *response.ServiceError) *response.ServiceError {
	tx := db.Begin()
	if tx.Error != nil {
		return response.WrapError(response.FailedCode, response.GetStatusCode(response.FailedCode).Message)
	}
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
			panic(r)
		}
	}()
	if err := fn(tx); err != nil {
		tx.Rollback()
		return response.WrapError(err.Code, err.Err.Error())
	}
	if err := tx.Commit().Error; err != nil {
		tx.Rollback()
		return response.WrapError(response.FailedCode, response.GetStatusCode(response.FailedCode).Message)
	}
	return nil
}
